#ifndef __Y64_KEYBOARD_H__
#define __Y64_KEYBOARD_H__

/*
Intel 8042键盘控制器：
    0x60端口，1字节，双向读写
        读：返回键盘扫描码或8042键盘控制器发来的数据
        写：向键盘发送命令（如果命令带有参数，随后再向该端口发送参数），或向8042键盘控制器发送命令
    0x64端口，1字节，双向读写
        读：返回8042键盘控制器的状态，其各位功能描述如下：
            bit7=1:说明与键盘通讯时发生奇偶校验错误
            bit6=1:说明接收键盘数据超时
            bit5=1:说明鼠标输出缓冲区已满
            bit4=0:禁止键盘
            bit3:记录上次操作的端口号（键盘控制器内部使用），1为0x64端口，0为0x60端口
            bit2=1:说明控制器已完成自检
            bit1=1:说明键盘输入缓冲区已满
            bit0=1:说明键盘输出缓冲区已满
        写：向8042键盘控制器发送控制命令
    向I/O端口0x64发送控制命令操作8042键盘控制器，如果该命令需要附加参数，则再向0x60发送命令数值。对于某些返回应答数据的控制命令，其应答数据将发往端口0x60，常用命令如下：
        命令        参数        返回值          功能描述
        0x20        无          有              读取键盘的配置值
        0x60        有          无              向键盘发送配置命令，配置值各位的功能如下：
                                                    bit7=0:0
                                                    bit6=1:在扫描码存入输入缓冲区前，将其转换为第一套扫描码
                                                    bit5=0:使能鼠标设备
                                                    bit4=0:使能键盘设备
                                                    bit3=0:0
                                                    bit2=1:通知系统已完成热启动测试及初始化
                                                    bit1=1:使能鼠标中断IRQ12(MIBF)
                                                    bit0=1:使能键盘中断IRQ1(IBF)
        0xa7        无          无              禁止鼠标端口
        0xa8        无          无              开启鼠标端口
        0xa9        无          有              鼠标端口自检测试，返回值0x00表示正常
        0xaa        无          有              控制器自检测试，返回值0x55表示正常
        0xab        无          有              键盘端口自检测试，返回值0x00表示正常
        0xad        无          无              禁止键盘通信，自动复位控制器状态第4位
        0xae        无          无              开启键盘通信，自动置位控制器状态第4位
        0xc0        无          有              读输入端口P1
        0xd0        无          有              读输出端口P2
        0xd1        有          无              写输出端口P2
        0xd2        有          无              把参数写入到键盘缓冲区，就如同从键盘收到数据一样
        0xd3        有          无              把参数写入到鼠标缓冲区，就如同从鼠标收到数据一样
        0xd4        有          无              向鼠标设备发送数据
        0xe0        无          无              读测试端口P3
        0xfe        无          无              系统重启

    输入/输出和测试端口各引脚功能
        引脚        输入端口P1          输出端口P2          测试端口P3
        0           键盘数据            系统重启            键盘时钟
        1           鼠标数据            A20地址线           键盘数据
        2           -                   鼠标数据            -
        3           -                   鼠标时钟            -
        4           -                   键盘IBF中断         -
        5           -                   鼠标IBF中断         -
        6           -                   键盘时钟            -
        7           -                   键盘数据            -
        通过8042键盘控制器开启A20地址线,拉高P2端口pin1，示例代码：
            io_out8(0x64, 0xd1);
            io_out8(0x60, 0xdf);
        系统重启，拉低P2端口pin0，或向0x64端口发送0xfe命令
            io_out8(0x64, 0xfe);

    控制键盘可以向0x60端口发送命令，如果带有参数，则紧随命令之后参数再写入0x60端口，键盘控制命令：
        命令        参数        返回值          功能描述
        0xff        无          有              重启键盘
        0xfe        无          无              重新发送上一字节
        0xf6        无          无              使用默认按键速率（10.9cps/500ms）
        0xf5        无          无              停止键盘扫描
        0xf4        无          无              开启键盘扫描
        0xf3        有          无              设置按键速率
        0xf2        无          有              获取键盘设备ID号（2B）
        0xf0        有          无              设置键盘扫描码集，可用参数如下：
                                                    0x0:取得当前扫描码(有返回值)
                                                    0x1:代表第一套扫描码
                                                    0x2:代表第二套扫描码
                                                    0x3:代表第三套扫描码
        0xee        无          有              键盘回复0xee
        0xed        有          无              控制led灯亮/灭，参数各位功能说明如下：
                                                    bit2:Caps Lock灯（1-亮 0-灭）
                                                    bit1:Num Lock灯（1-亮 0-灭）
                                                    bit0:Scroll Lock灯（1-亮 0-灭）

    控制鼠标，可以向0x64发送控制命令0xd4，然后就可以向0x60发送控制命令到鼠标，常用命令如下：
        命令                                    功能描述
        0xff                                    重启鼠标
        0xfe                                    重新发送上一条数据包
        0xf6                                    使用默认采样率100Hz、分辨率4pixel/mm
        0xf5                                    禁止鼠标设备发送数据包
        0xf4                                    允许鼠标设备发送数据包
        0xf3                                    设置鼠标采样率
        0xf2                                    获得鼠标设备的ID号

    鼠标数据包说明:
                    7       6       5       4       3   2           1           0
        Byte 1      Y溢出   X溢出   Y符号位 X符号位 1   鼠标中键    鼠标右键    鼠标左键
        Byte 2                              X移动值
        Byte 3                              Y移动值
        Byte 4                              Z移动值                                         (当鼠标id为3时)
        Byte 4      0       0       鼠标5键 鼠标4键 Z3  Z2          Z1          Z0          (当鼠标id为4时)
        X/Y溢出：表示X或Y方向数值溢出，并丢弃整个数据包
        X/Y符号位：表示鼠标在平面直角坐标系内的移动方向
        X/Y/Z移动值：记录X、Y或Z方向的移动值
        左/中/右/4键/5键：记录鼠标按键状态
        Z0~3：记录鼠标滚轮滚动方向（0-无滚动 1-垂直向上滚动 F-垂直向下滚动 2-水平向右滚动 E-水平向左滚动）
*/

#include "lib.h"

#define PORT_KB_DATA        0x60
#define PORT_KB_STATUS      0x64
#define PORT_KB_CMD         0x64

#define KBCMD_WRITE_CMD     0x60
#define KBCMD_READ_CMD      0x20
#define KB_INIT_MODE        0x47        // 参考8042控制器命令

#define KB_BUF_SIZE         100

#define KB_STATUS_IBF        0x2        // 键盘输入缓冲区是否已满状态位
#define KB_STATUS_OBF        0x1        // 键盘输出缓冲区是否已满状态位

#define WAIT_KB_WRITE() while (io_in8(PORT_KB_STATUS) & KB_STATUS_IBF)  // 等待键盘输入缓冲区为空
#define WAIT_KB_READ()  while (io_in8(PORT_KB_STATUS) & KB_STATUS_OBF)  // 等待键盘输出缓冲区为空

// NR_SCAN_CODES * MAP_COLS描述一个由第三类键盘扫描码组成的一维数组长度，共128个按键，每个按键包括普通按键和Shift+普通按键两种状态
#define NR_SCAN_CODES       0x80
#define MAP_COLS            2

#define PAUSEBREAK          1       // 第一类特殊按键
#define PRINTSCREEN         2
#define OTHERKEY            4       // 第二类特殊按键
#define FLAG_BREAK          0x80


typedef struct __st_keyboard_in_buf {
    unsigned char *p_head;          // 标记下一个键盘数据存放位置
    unsigned char *p_tail;          // 标记当前待解析的数据
    int count;                      // 记录存放的键盘数据个数
    unsigned char buf[KB_BUF_SIZE]; // 键盘数据缓存
} st_keyboard_in_buf;

extern st_keyboard_in_buf *g_p_kb;

void keyboard_init();

void keyboard_exit();

void analysis_keycode();

#endif